import redis
import os
import json
import time
from app.utils.logger import logger
from datetime import datetime, timedelta
import threading

class RedisClient:
    """Redis配置和连接管理类"""
    
    def __init__(self):
        # 从环境变量获取Redis配置
        self.redis_url = os.environ.get('REDIS_URL', 'redis://localhost:6379/0')
        
        # 解析Redis URL
        self.host = 'localhost'
        self.port = 6379
        self.db = 0
        
        if self.redis_url.startswith('redis://'):
            # 简单解析Redis URL
            parts = self.redis_url[8:].split(':')
            if len(parts) >= 2:
                self.host = parts[0]
                port_db = parts[1].split('/')
                if len(port_db) >= 1:
                    self.port = int(port_db[0])
                if len(port_db) >= 2:
                    self.db = int(port_db[1])
        
        # 高级Redis连接池配置
        self.pool_config = {
            'max_connections': 20,
            'socket_timeout': 3,
            'health_check_interval': 30
        }
        
        # 连接池
        self.pool = None
        self.redis_client = None
        
        # 连接状态
        self.connected = False
        
        # 连接监控线程
        self.monitor_thread = None
        self.monitor_running = False
        
        # 尝试初始化连接
        self.initialize_connection()
        
        # 启动连接监控
        self.start_monitoring()
        
    def initialize_connection(self):
        """初始化Redis连接"""
        try:
            # 创建连接池
            self.pool = redis.ConnectionPool(
                host=self.host,
                port=self.port,
                db=self.db,
                decode_responses=True,
                **self.pool_config
            )
            
            # 创建Redis客户端
            self.redis_client = redis.Redis(connection_pool=self.pool)
            
            # 测试连接
            response = self.redis_client.ping()
            self.connected = True
            logger.info(f"[OK] Redis连接成功! 主机: {self.host}:{self.port}, 数据库: {self.db}")
            
            # 设置键空间通知（用于键过期监听）
            self.setup_keyspace_notifications()
            
            return True
        except redis.exceptions.ConnectionError as e:
            self.connected = False
            logger.error(f"[ERROR] Redis连接失败: {e}")
            logger.error("提示: Redis服务可能未启动或配置错误。")
            return False
        except Exception as e:
            self.connected = False
            logger.error(f"[ERROR] Redis初始化错误: {e}")
            return False
    
    def setup_keyspace_notifications(self):
        """设置键空间通知"""
        try:
            # 启用过期事件通知
            self.redis_client.config_set('notify-keyspace-events', 'Ex')
        except Exception as e:
            logger.warning(f"无法设置键空间通知: {e}")
    
    def get_client(self):
        """获取Redis客户端实例"""
        if not self.connected or self.redis_client is None:
            # 尝试重新连接
            self.initialize_connection()
        return self.redis_client
    
    def start_monitoring(self):
        """启动连接监控线程"""
        if self.monitor_thread and self.monitor_thread.is_alive():
            return
        
        self.monitor_running = True
        self.monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True)
        self.monitor_thread.start()
    
    def _monitor_loop(self):
        """监控循环，定期检查Redis连接状态"""
        while self.monitor_running:
            try:
                if self.connected:
                    # 定期检查连接健康状态
                    self.redis_client.ping()
                else:
                    # 尝试重新连接
                    self.initialize_connection()
            except Exception as e:
                self.connected = False
                logger.error(f"Redis连接监控错误: {e}")
            
            # 每30秒检查一次
            time.sleep(30)
    
    def stop_monitoring(self):
        """停止连接监控"""
        self.monitor_running = False
        if self.monitor_thread:
            self.monitor_thread.join(timeout=5)

# 创建全局Redis配置实例
redis_config = RedisClient()

def get_redis():
    """获取Redis客户端的辅助函数"""
    return redis_config.get_client()

def is_redis_available():
    """检查Redis是否可用"""
    return redis_config.connected

# ===== 实用Redis操作函数 =====

def cache_scan_result(scan_id, result, expire_seconds=3600):
    """缓存扫描结果到Redis"""
    if not is_redis_available():
        return False
    
    try:
        client = get_redis()
        key = f"scan:{scan_id}:result"
        # 将结果转换为JSON字符串
        client.set(key, json.dumps(result), ex=expire_seconds)
        logger.debug(f"已缓存扫描结果: {scan_id}")
        return True
    except Exception as e:
        logger.error(f"缓存扫描结果失败: {e}")
        return False

def get_cached_scan_result(scan_id):
    """从Redis获取缓存的扫描结果"""
    if not is_redis_available():
        return None
    
    try:
        client = get_redis()
        key = f"scan:{scan_id}:result"
        result = client.get(key)
        if result:
            return json.loads(result)
        return None
    except Exception as e:
        logger.error(f"获取缓存扫描结果失败: {e}")
        return None

def update_scan_progress(scan_id, progress, status=None):
    """更新扫描进度"""
    if not is_redis_available():
        return False
    
    try:
        client = get_redis()
        pipeline = client.pipeline()
        
        # 更新进度
        pipeline.hset(f"scan:{scan_id}:progress", "progress", progress)
        
        # 更新状态（如果提供）
        if status:
            pipeline.hset(f"scan:{scan_id}:progress", "status", status)
            pipeline.hset(f"scan:{scan_id}:progress", "updated_at", datetime.utcnow().isoformat())
        
        pipeline.expire(f"scan:{scan_id}:progress", 3600)
        pipeline.execute()
        
        logger.debug(f"已更新扫描进度: {scan_id}, 进度: {progress}%")
        return True
    except Exception as e:
        logger.error(f"更新扫描进度失败: {e}")
        return False

def get_scan_progress(scan_id):
    """获取扫描进度"""
    if not is_redis_available():
        return None
    
    try:
        client = get_redis()
        progress_data = client.hgetall(f"scan:{scan_id}:progress")
        if progress_data:
            # 转换数值类型
            if 'progress' in progress_data:
                progress_data['progress'] = int(progress_data['progress'])
            return progress_data
        return None
    except Exception as e:
        logger.error(f"获取扫描进度失败: {e}")
        return None

def store_vulnerability_stats(stats):
    """存储漏洞统计信息"""
    if not is_redis_available():
        return False
    
    try:
        client = get_redis()
        key = "vulnerability:stats"
        
        # 使用哈希存储统计信息
        pipeline = client.pipeline()
        for vuln_type, count in stats.items():
            pipeline.hincrby(key, vuln_type, count)
        
        # 设置过期时间为24小时
        pipeline.expire(key, 86400)
        pipeline.execute()
        
        logger.debug("已更新漏洞统计信息")
        return True
    except Exception as e:
        logger.error(f"存储漏洞统计信息失败: {e}")
        return False

def get_vulnerability_stats():
    """获取漏洞统计信息"""
    if not is_redis_available():
        return None
    
    try:
        client = get_redis()
        stats = client.hgetall("vulnerability:stats")
        # 转换数值类型
        for key in stats:
            stats[key] = int(stats[key])
        return stats
    except Exception as e:
        logger.error(f"获取漏洞统计信息失败: {e}")
        return None

def clear_scan_cache(scan_id):
    """清理特定扫描的缓存"""
    if not is_redis_available():
        return False
    
    try:
        client = get_redis()
        pipeline = client.pipeline()
        pipeline.delete(f"scan:{scan_id}:result")
        pipeline.delete(f"scan:{scan_id}:progress")
        pipeline.execute()
        
        logger.debug(f"已清理扫描缓存: {scan_id}")
        return True
    except Exception as e:
        logger.error(f"清理扫描缓存失败: {e}")
        return False

def cleanup_old_cache(max_age_hours=24):
    """清理旧的缓存数据"""
    if not is_redis_available():
        return False
    
    try:
        client = get_redis()
        # 扫描所有扫描结果键
        cursor = '0'
        scan_keys = []
        
        while cursor != 0:
            cursor, keys = client.scan(cursor=cursor, match="scan:*", count=100)
            scan_keys.extend(keys)
        
        # 获取当前时间
        now = datetime.utcnow()
        
        # 删除超过指定时间的键
        pipeline = client.pipeline()
        for key in scan_keys:
            # 尝试获取键的创建时间（如果有）
            # 注意：Redis本身不存储键的创建时间，这里假设我们在设置键时存储了时间戳
            created_at = client.hget(key, "created_at")
            if created_at:
                created_time = datetime.fromisoformat(created_at)
                if (now - created_time) > timedelta(hours=max_age_hours):
                    pipeline.delete(key)
        
        pipeline.execute()
        
        logger.debug(f"已清理超过{max_age_hours}小时的缓存数据")
        return True
    except Exception as e:
        logger.error(f"清理旧缓存失败: {e}")
        return False